/********************************************************************
	
	Tau Virtual Machine
	
	tau.c
	
	(c) 2009 Tau VM Project
	
	Author:  Matt C.
	Date:  Jul 12, 2009
	
********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tau.h"

typedef TauValue* (*fptr) (TauValue* vs, TauValue* vt);

int ttab[4][4];

//fptr vtab[10];

TauDispatchTable* vtab[10];

fptr vtabAdd[4][4];

TauDispatchTable* itab;

TauValue** regs;


// Routines *********************************************************

TauValue* TauNullInvalid1 (TauValue* vs)
{
	TauValue* vd;
	
	vd = TauMakeNull();
	return vd;
}

TauValue* TauNullInvalid2 (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	
	vd = TauMakeNull();
	return vd;
}

TauValue* TauIntegerNeg (TauValue* vs)
{
	TauValue* vd;
	
	vd = TauMakeInteger(0);
	vd->data.i = -vs->data.i;
	return vd;
}

TauValue* TauIntegerAdd (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i + vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i + vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.i) + vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauIntegerSub (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i - vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i - vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.i) - vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauIntegerMul (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i * vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i * vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.i) * vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauIntegerDiv (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.i) / vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.i) / vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.i) / vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauIntegerMod (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	
	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i % vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i % vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeNull();
			break;
		default:
			// exception
			break;
	}
	
	return vd;
}

TauValue* TauIntegerIdiv (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i / vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i / vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.i) / vt->data.d;
			break;
		default:
			// exception
			break;
	}
	
	return vd;
}

TauValue* TauIntegerNot (TauValue* vs)
{
	TauValue* vd;
	
	vd = TauMakeInteger(0);
	vd->data.i = ~vs->data.i;
	return vd;
}

TauValue* TauIntegerAnd (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	
	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i & vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i & vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeNull();
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauIntegerOr (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i | vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i | vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeNull();
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauIntegerXor (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i ^ vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.i ^ vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeNull();
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauBooleanNeg (TauValue* vs)
{
	TauValue* vd;
	
	vd = TauMakeInteger(0);
	vd->data.i = -vs->data.b;
	return vd;
}

TauValue* TauBooleanAdd (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b + vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b + vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.b) + vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauBooleanSub (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b - vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b - vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.b) - vt->data.d;
			break;
		default:
			// exception
			break;
	}
	
	return vd;
}

TauValue* TauBooleanMul (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b * vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b * vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.b) * vt->data.d;
			break;
		default:
			// exception
			break;
	}
	
	return vd;
}

TauValue* TauBooleanDiv (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.b) / vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.b) / vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.b) / vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauBooleanMod (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	
	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b % vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b % vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeNull();
			break;
		default:
			// exception
			break;
	}
	
	return vd;
}

TauValue* TauBooleanIdiv (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b / vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b / vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = (double)(vs->data.b) / vt->data.d;
			break;
		default:
			// exception
			break;
	}
	
	return vd;
}

TauValue* TauBooleanNot (TauValue* vs)
{
	TauValue* vd;
	
	vd = TauMakeInteger(0);
	vd->data.i = ~vs->data.b;
	return vd;
}

TauValue* TauBooleanAnd (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b & vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b & vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeNull();
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauBooleanOr (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b | vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b | vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeNull();
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauBooleanXor (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b ^ vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeInteger(0);
			vd->data.i = vs->data.b ^ vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeNull();
			break;
		default:
			// exception
			break;
	}
	
	return vd;
}


TauValue* TauBooleanInvalid (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	
	vd = TauMakeNull();
	return vd;
}

TauValue* TauDoubleNeg (TauValue* vs)
{
	TauValue* vd;
	
	vd = TauMakeDouble(0.0);
	vd->data.d = -vs->data.d;
	return vd;
}

TauValue* TauDoubleAdd (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d + vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d + vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d + vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauDoubleSub (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d - vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d - vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d - vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauDoubleMul (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d * vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d * vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d * vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauDoubleDiv (TauValue* vs, TauValue* vt)
{
	TauValue* vd;

	switch (vt->type)
	{
		case T_INTEGER:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d / vt->data.i;
			break;
		case T_BOOLEAN:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d / vt->data.b;
			break;
		case T_DOUBLE:
			vd = TauMakeDouble(0.0);
			vd->data.d = vs->data.d / vt->data.d;
			break;
		default:
			// exception
			break;
	}

	return vd;
}

TauValue* TauDoubleInvalid1 (TauValue* vs)
{
	TauValue* vd;
	
	vd = TauMakeNull();
	return vd;
}

TauValue* TauDoubleInvalid2 (TauValue* vs, TauValue* vt)
{
	TauValue* vd;
	
	vd = TauMakeNull();
	return vd;
}

TauDispatchTable* TauMakeDispatchTable (void)
{
	TauDispatchTable* dt;

	dt = (TauDispatchTable*) malloc(sizeof(TauDispatchTable));
	return dt;
}

TauTypeRegistry* tr;
char** tr1;

void TauRegisterType (int id, char* name)
{
	tr1[id] = name;
}


void TauInit (void)
{
	// Initialize the virtual machine
	
	// STEP 1:  Register types
	
	//tr = (TauTypeRegistry) malloc(sizeof(TauTypeRegistry));
	tr1 = (char**) malloc(64 * sizeof(char*));
	
	// STEP 2:  Initialize dispatch tables
	
	TauRegisterType(T_NULL, "null");
	vtab[T_NULL] = TauMakeDispatchTable();
	vtab[T_NULL]->neg = TauNullInvalid1;
	vtab[T_NULL]->add = TauNullInvalid2;
	vtab[T_NULL]->sub = TauNullInvalid2;
	vtab[T_NULL]->mul = TauNullInvalid2;
	vtab[T_NULL]->div = TauNullInvalid2;
	vtab[T_NULL]->mod = TauNullInvalid2;
	vtab[T_NULL]->idiv = TauNullInvalid2;
	vtab[T_NULL]->not = TauNullInvalid1;
	vtab[T_NULL]->and = TauNullInvalid2;
	vtab[T_NULL]->or = TauNullInvalid2;
	vtab[T_NULL]->xor = TauNullInvalid2;
	
	TauRegisterType(T_INTEGER, "integer");
	vtab[T_INTEGER] = TauMakeDispatchTable();
	vtab[T_INTEGER]->neg = TauIntegerNeg;
	vtab[T_INTEGER]->add = TauIntegerAdd;
	vtab[T_INTEGER]->sub = TauIntegerSub;
	vtab[T_INTEGER]->mul = TauIntegerMul;
	vtab[T_INTEGER]->div = TauIntegerDiv;
	vtab[T_INTEGER]->mod = TauIntegerMod;
	vtab[T_INTEGER]->idiv = TauIntegerIdiv;
	vtab[T_INTEGER]->not = TauIntegerNot;
	vtab[T_INTEGER]->and = TauIntegerAnd;
	vtab[T_INTEGER]->or = TauIntegerOr;
	vtab[T_INTEGER]->xor = TauIntegerXor;
	
	TauRegisterType(T_BOOLEAN, "boolean");
	vtab[T_BOOLEAN] = TauMakeDispatchTable();
	vtab[T_BOOLEAN]->neg = TauBooleanNeg;
	vtab[T_BOOLEAN]->add = TauBooleanAdd;
	vtab[T_BOOLEAN]->sub = TauBooleanSub;
	vtab[T_BOOLEAN]->mul = TauBooleanMul;
	vtab[T_BOOLEAN]->div = TauBooleanDiv;
	vtab[T_BOOLEAN]->mod = TauBooleanMod;
	vtab[T_BOOLEAN]->idiv = TauBooleanIdiv;
	vtab[T_BOOLEAN]->not = TauBooleanNot;
	vtab[T_BOOLEAN]->and = TauBooleanAnd;
	vtab[T_BOOLEAN]->or = TauBooleanOr;
	vtab[T_BOOLEAN]->xor = TauBooleanXor;
	
	TauRegisterType(T_DOUBLE, "double");
	vtab[T_DOUBLE] = TauMakeDispatchTable();
	vtab[T_DOUBLE]->neg = TauDoubleNeg;
	vtab[T_DOUBLE]->add = TauDoubleAdd;
	vtab[T_DOUBLE]->sub = TauDoubleSub;
	vtab[T_DOUBLE]->mul = TauDoubleMul;
	vtab[T_DOUBLE]->div = TauDoubleDiv;
	vtab[T_DOUBLE]->mod = TauDoubleInvalid2;
	vtab[T_DOUBLE]->idiv = TauDoubleDiv;
	vtab[T_DOUBLE]->not = TauDoubleInvalid1;
	vtab[T_DOUBLE]->and = TauDoubleInvalid2;
	vtab[T_DOUBLE]->or = TauDoubleInvalid2;
	vtab[T_DOUBLE]->xor = TauDoubleInvalid2;
}

TauFrame* TauMakeFrame (void)
{
	TauFrame* f;

	f = (TauFrame*) malloc(sizeof(TauFrame));
	
	f->regs = NULL;
	f->locals = NULL;
	f->nlocals = 0;
	f->nparams = 0;
	
	f->code = NULL;
	f->cp = 0;
	
	f->prev = NULL;
	f->link = NULL;

	return f;
}

void TauPushFrame (TauState* s, TauFrame* f)
{
	f->prev = s->fp;
	s->fp = f;
}

void TauPopFrame (TauState* s)
{
	s->fp = s->fp->prev;
}

TauState* TauMakeState (void)
{
	TauState* s;
	TauFrame* f;
	int i;
	
	s = (TauState*) malloc(sizeof(TauState));
	
	// Build global lookup table
	s->globals = (TauGlobal**) malloc(binSizes[0] * sizeof(TauGlobal*));
	s->nglobals = 0;
	
	i = binSizes[0];
	while (i--)
		s->globals[i] = NULL;
	
	// Push initial frame (i.e. global frame)
	f = TauMakeFrame();
	TauPushFrame(s, f);
	
	return s;
}

TauValue* TauMakeValue (void)
{
	TauValue* v;

	v = (TauValue*) malloc(sizeof(TauValue));
	return v;
}

TauValue* TauMakeNull (void)
{
	TauValue* v;

	v = (TauValue*) malloc(sizeof(TauValue));
	v->type = T_NULL;
	v->data.i = 0;
	
	return v;
}

TauValue* TauMakeInteger (int i)
{
	TauValue* v;

	v = (TauValue*) malloc(sizeof(TauValue));
	v->type = T_INTEGER;
	v->data.i = i;
	
	return v;
}

TauValue* TauMakeBoolean (int b)
{
	TauValue* v;

	v = (TauValue*) malloc(sizeof(TauValue));
	v->type = T_BOOLEAN;
	v->data.b = b;
	
	return v;
}

TauValue* TauMakeDouble (double d)
{
	TauValue* v;

	v = (TauValue*) malloc(sizeof(TauValue));
	v->type = T_DOUBLE;
	v->data.d = d;
	
	return v;
}

TauValue* TauMakeComplex (double cr, double ci)
{
	TauComplex* c;
	TauValue* v;
	
	c = (TauComplex*) malloc(sizeof(TauComplex));
	c->real = cr;
	c->imag = ci;
	
	v = (TauValue*) malloc(sizeof(TauValue));
	v->type = T_COMPLEX;
	v->data.c = c;
	
	return v;
}

TauValue* TauMakeString (char* s)
{
	TauString* str;
	TauValue* v;
	
	str = (TauString*) malloc(sizeof(TauString));
	str->data = s;
	str->len = strlen(s);
	
	v = (TauValue*) malloc(sizeof(TauValue));
	v->type = T_STRING;
	v->data.s = str;
	
	return v;
}

/*

void TauPushValue (TauValue* v)
{
	if (psp < psCapacity)
	{	
		ps[psp] = v;
		psp++;
	}
	
	else
		; // Have to realloc the parameter stack
	
}

void TauPopValue (void)
{
	psp--;
	
	
}
*/

void TauSetGlobal (TauState* s, char* name, TauValue* value)
{
	unsigned long hash;
	int c;
	
	TauGlobal* g;
	int k;
	
	// Step 1: Hash the name
	hash = 5381;
	
	while (c = *name++)
		hash = ((hash << 5) + hash) + c;
	
	k = hash % s->nglobals;
	
	// Step 2: Look for existing entry
	g = s->globals[k];
	
	while (g)
	{
		if (!strcmp(name, g->name))
		{
			// Overwrite existing global variable
			g->value->refs--;
			g->value = value;
			g->value->refs++;
			break;
		}
		
		g = g->next;
	}
	
	if (!g)
	{
		// Make new global variable
		g = (TauGlobal*) malloc(sizeof(TauGlobal));
		g->name = name;
		g->value = value;
		g->value->refs++;
		g->next = s->globals[k];
		s->globals[k] = g;
		s->nglobals++;
	}
}

TauValue* TauGetGlobal (TauState* s, char* name)
{
	unsigned long hash;
	int c;

	TauGlobal* g;
	int k;
	
	// STEP 1:  Hash the name
	hash = 5381;
	
	while (c = *name++)
		hash = ((hash << 5) + hash) + c;
	
	k = hash % s->nglobals;
	
	// Step 2: Look for existing entry
	g = s->globals[k];
	
	while (g)
	{
		if (!strcmp(name, g->name))
			return g->value;
		
		g = g->next;
	}
	
	// Step 3: Name does not exist
	return NULL;
}

int TauDeleteGlobal (TauState* s, char* name)
{
	unsigned long hash;
	int c;

	TauGlobal* g;
	int k;
	
	// STEP 1:  Hash the name
	hash = 5381;
	
	while (c = *name++)
		hash = ((hash << 5) + hash) + c;
	
	k = hash % s->nglobals;
	
	// Step 2: Look for existing entry
	g = s->globals[k];
	
	while (g)
	{
		if (!strcmp(name, g->name))
		{
			// Fix (delete and return 1?)
			break;
		}
		
		g = g->next;
	}
	
	// Step 3: Name does not exist
	return 0;
}


TauChunk* TauLoadChunk (char* filename)
{
	TauChunk* c;
	TauRoutine* r;
	FILE* fp;

	// Header fields
	unsigned char magic[4];		// [ESC] "Tau"
	unsigned char version[4];	// maj.min.patch.0
	unsigned char format[1];	// 0, 1, .. n
	unsigned char order[1];		// 0 = Big-endian, 1 = Little-endian
	unsigned char reserved[6];	// 000000
	
	// Constant pool fields
	unsigned short nconstants;
	unsigned short* type;
	unsigned short* index;
	
	unsigned short nintegers;
	int* integers;
	
	unsigned short ndoubles;
	double* doubles;
	
	unsigned short nchars;
	char* chars;
	
	// Global fields
	unsigned short nglobals;
	unsigned short* globalindex;
	unsigned int globalsize;
	char* globalnames;
	
	// Routine fields
	int nroutines;
	
	unsigned int line;
	unsigned short nparams;
	unsigned short nlocals;
	
	unsigned int len;
	unsigned char* code;
	
	int ival;
	int bval;
	double dval;
	char* sval;
	double cval[2];
	
	int i;
	
	// Open file
	fp = fopen(filename, "r");

	if (!fp)
	{
		printf("Error loading chunk from disk.\n");
		exit(1);
	}
	
	// Read header (16 bytes)
	fread(magic, sizeof(unsigned char), 4, fp);
	fread(version, sizeof(unsigned char), 4, fp);
	fread(format, sizeof(unsigned char), 1, fp);
	fread(order, sizeof(unsigned char), 1, fp);
	fread(reserved, sizeof(unsigned char), 6, fp);
	
	// Read constant pool
	fread(&nconstants, sizeof(unsigned short), 1, fp);
	type = (unsigned short*) malloc(nconstants * sizeof(unsigned short));
	index = (unsigned short*) malloc(nconstants * sizeof(unsigned short));
	fread(type, sizeof(unsigned short), nconstants, fp);
	fread(index, sizeof(unsigned short), nconstants, fp);
	
	fread(&nintegers, sizeof(unsigned short), 1, fp);
	integers = (int*) malloc(nintegers * sizeof(int));
	fread(integers, sizeof(int), nintegers, fp);
	
	fread(&ndoubles, sizeof(unsigned short), 1, fp);
	doubles = (double*) malloc(ndoubles * sizeof(double));
	fread(doubles, sizeof(double), ndoubles, fp);
	
	fread(&nchars, sizeof(unsigned short), 1, fp);
	chars = (char*) malloc(nchars * sizeof(char));
	fread(chars, sizeof(char), nchars, fp);
	
	// Start building chunk
	c = (TauChunk*) malloc(sizeof(TauChunk));
	
	// Build constant pool
	c->nconstants = nconstants;
	c->constants = (TauValue**) malloc(nconstants * sizeof(TauValue*));
	
	i = nconstants;

	while (i--)
	{
		switch (type[i])
		{
			case T_INTEGER:
			{
				ival = integers[index[i]];
				c->constants[i] = TauMakeInteger(ival);
				break;
			}
			
			case T_BOOLEAN:
			{
				bval = index[i];
				c->constants[i] = TauMakeBoolean(bval);
				break;
			}
			
			case T_DOUBLE:
			{
				dval = doubles[index[i]];
				c->constants[i] = TauMakeDouble(dval);
				break;
			}
			
			case T_COMPLEX:
			{
				cval[0] = doubles[index[i]];
				cval[1] = doubles[index[i+1]];
				c->constants[i] = TauMakeComplex(cval[0], cval[1]);
				break;
			}
			
			case T_STRING:
			{
				sval = chars + index[i];
				c->constants[i] = TauMakeString(sval);
				break;
			}
		}
	}
	
	// Read globals
	fread(&nglobals, sizeof(unsigned short), 1, fp);
	globalindex = (unsigned short*) malloc(nglobals * sizeof(unsigned short));
	fread(globalindex, sizeof(unsigned short), nglobals, fp);
	fread(&globalsize, sizeof(unsigned int), 1, fp);
	globalnames = (char*) malloc(globalsize * sizeof(char));
	fread(globalnames, sizeof(char), globalsize, fp);
	
	// build global intern pool
	c->nglobals = nglobals;
	c->globals = (char**) malloc(nglobals * sizeof(char*));
	
	i = nglobals;
	
	while (i--)
		c->globals[i] = globalnames + globalindex[i];
	
	

	// Read body
	
	fread(&len, sizeof(unsigned int), 1, fp);
	code = (unsigned char*) malloc(len * sizeof(unsigned char));
	fread(code, sizeof(unsigned char), len, fp);	
	c->len = len;
	c->code = code;
	
	fread(&nroutines, sizeof(unsigned int), 1, fp);
	
	i = nroutines;
	
	while (i--)
	{
		fread(&line, sizeof(unsigned int), 1, fp);
		fread(&nparams, sizeof(unsigned short), 1, fp);
		fread(&nlocals, sizeof(unsigned short), 1, fp);

		fread(&len, sizeof(unsigned int), 1, fp);
		code = (unsigned char*) malloc(len * sizeof(unsigned char));
		fread(&code, sizeof(unsigned char), len, fp);
		
		r = (TauRoutine*) malloc(sizeof(TauRoutine));
		r->nparams = nparams;
		r->nlocals = nlocals;
		r->len = len;
		r->code = code;
	}
	
	fclose(fp);
	
	return c;
}



void TauLOD (TauState* s, TauChunk* ch, unsigned char rd, unsigned short rx)
{
	// Load G[RX] into RD
	// Globals is an open chained hash table
	TauValue* vd;
	TauValue* vx;
	char* name;
	
	// Look up name in chunk's global name pool
	/*** need to ensure 0 <= rx <= 65,535 before lookup - safe mode? ***/
	
	name = ch->globals[rx];
	
	// Look up value of global variable
	vx = TauGetGlobal(s, name);
	
	if (vx)
	{
		// Assign it if it exists
		// Q. Throw exception or set register to NULL if it doesn't exist?
		vd = vx;
		s->fp->regs[rd] = vd;
	}
}

void TauSTO (unsigned char rd, unsigned short rx)
{
	// Store RD into G[RX]
	TauValue* vd;
	TauValue* vx;
	
	
}

void TauNEG (unsigned char rd, unsigned char rs)
{
	TauValue* vd;
	TauValue* vs;
	
	vs = regs[rs];
	
	vd = vtab[vs->type]->neg(vs);
	regs[rd] = vd;
}

void TauADD (unsigned char rd, unsigned char rs, unsigned char rt)
{
	TauValue* vd;
	TauValue* vs;
	TauValue* vt;
	
	vs = regs[rs];
	vt = regs[rt];
	
	vd = vtab[vs->type]->add(vs, vt);
	regs[rd] = vd;
}

void TauSUB (unsigned char rd, unsigned char rs, unsigned char rt)
{
	TauValue* vd;
	TauValue* vs;
	TauValue* vt;
	
	vs = regs[rs];
	vt = regs[rt];
	
	vd = vtab[vs->type]->sub(vs, vt);
	regs[rd] = vd;
}

void TauMUL (unsigned char rd, unsigned char rs, unsigned char rt)
{
	TauValue* vd;
	TauValue* vs;
	TauValue* vt;
	
	vs = regs[rs];
	vt = regs[rt];
	
	vd = vtab[vs->type]->mul(vs, vt);
	regs[rd] = vd;
}

void TauDIV (unsigned char rd, unsigned char rs, unsigned char rt)
{
	TauValue* vd;
	TauValue* vs;
	TauValue* vt;
	
	vs = regs[rs];
	vt = regs[rt];
	
	vd = vtab[vs->type]->div(vs, vt);
	regs[rd] = vd;
}

void TauMOD (unsigned char rd, unsigned char rs, unsigned char rt)
{
	TauValue* vd;
	TauValue* vs;
	TauValue* vt;
	
	vs = regs[rs];
	vt = regs[rt];
	
	vd = vtab[vs->type]->mod(vs, vt);
	regs[rd] = vd;
}

void TauIDIV (unsigned char rd, unsigned char rs, unsigned char rt)
{
	TauValue* vd;
	TauValue* vs;
	TauValue* vt;
	
	vs = regs[rs];
	vt = regs[rt];
	
	vd = vtab[vs->type]->idiv(vs, vt);
	regs[rd] = vd;
}

void TauNOT (unsigned char rd, unsigned char rs)
{
	TauValue* vd;
	TauValue* vs;
	
	vs = regs[rs];
	
	vd = vtab[vs->type]->not(vs);
	regs[rd] = vd;
}

void TauAND (unsigned char rd, unsigned char rs, unsigned char rt)
{
	TauValue* vd;
	TauValue* vs;
	TauValue* vt;
	
	vs = regs[rs];
	vt = regs[rt];
	
	vd = vtab[vs->type]->and(vs, vt);
	regs[rd] = vd;
}

void TauOR (unsigned char rd, unsigned char rs, unsigned char rt)
{
	TauValue* vd;
	TauValue* vs;
	TauValue* vt;
	
	vs = regs[rs];
	vt = regs[rt];
	
	vd = vtab[vs->type]->or(vs, vt);
	regs[rd] = vd;
}
void TauXOR (unsigned char rd, unsigned char rs, unsigned char rt)
{
	TauValue* vd;
	TauValue* vs;
	TauValue* vt;
	
	vs = regs[rs];
	vt = regs[rt];
	
	vd = vtab[vs->type]->xor(vs, vt);
	regs[rd] = vd;
}


#define FIELD_MASK	0x000f

// Switch-based dispatch core

void TauEval (TauFrame* f)
{
	unsigned int inst;
	unsigned char op, rs, rt, rd;
	int done;
	
	done = TAU_FALSE;
	
	while (!done)
	{
		inst = 0xC0D3;
		
		op = (inst >> 24) | FIELD_MASK;
		rs = (inst >> 16) | FIELD_MASK;
		rt = (inst >> 8)  | FIELD_MASK;
		rd = (inst)       | FIELD_MASK;
		
		switch (op)
		{
			case LOD:
				//TauLOD(s, ch, rs, rx); ?
				break;

			case NEG:
				TauNEG(rs, rd);
				break;
			
			case ADD:
				TauADD(rs, rt, rd);
				break;

			case SUB:
				TauSUB(rs, rt, rd);
				break;

			case MUL:
				TauMUL(rs, rt, rd);
				break;

			case DIV:
				TauDIV(rs, rt, rd);
				break;

			case MOD:
				TauMOD(rs, rt, rd);
				break;

			case IDIV:
				TauIDIV(rs, rt, rd);
				break;
			
			case CALL:
				// Call eval on new routine
				break;

			default:
				// Exception
				break;
		}
		
		done = TAU_TRUE;
	}
}



// End **************************************************************

